//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include "ata.h"
#include "atarw.h"
#include "parttab.h"
#include "idedrive.h"
#include "idectrlr.h"
#include "ite8172.h"

STATIC void CDECL IDEControllerIsr(
        irq_t irq, void *pvDevice, InterruptContext *pContext);

//
// IDEController
//
ECode IDEController::Read(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ UInt32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode IDEController::Write(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ UInt32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode IDEController::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ const MemoryBuf & inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

void IDEController::Dispose()
{
    delete this;
}

ECode IDEController::Initialize(uint_t uControllerNo)
{
    m_uControllerNo = uControllerNo;
    uint8_t deviceIndicator = 0;

    kprintf("entering IDEController%d initialise.... \n", uControllerNo);
    switch (m_uControllerNo) {
        case 0:
            m_iobase    = IDEPrimaryChannel_IoBase;
            m_irq       = IDEPrimaryChannel_Irq;
            break;
        case 1:
            assert(0&&"there is no secondary ide bus on Godson Mainboard\n");
            m_iobase    = IDESecondaryChannel_IoBase;
            m_irq       = IDESecondaryChannel_Irq;
            break;
        default:
            return E_FAIL;
    }

    ResetController(m_iobase);

    MaskControllerInterrupt(m_iobase, FALSE);

    uint8_t u8DiagnosticCode = ExecuteDriveDiagnostics(m_iobase);

    kprintf("IDE%d: Diagnostic Code = 0x%x\n",
            m_uControllerNo, u8DiagnosticCode);

    if (DiagnosticCode_Device0Passed_Device1Failed != u8DiagnosticCode
        && DiagnosticCode_Device0Failed_Device1Failed <= u8DiagnosticCode) {
        return S_FALSE;
    }

    uint32_t uFlags = SaveFlagsAndCli();
    if (m_uControllerNo == 0) {
        AddIt8172Device(m_irq, &IDEControllerIsr, this);
    }
    else {
        AddIt8172Device(m_irq, &IDEControllerIsr, this);
    }
    RestoreIF(uFlags);
    ClearInterrupt(m_iobase);
    MaskControllerInterrupt(m_iobase, TRUE);

    for (int i = 0; i < DRIVES_PER_CONTROLLER; i++) {
        ECode ec = InitializeDevice(i);
        if (FAILED(ec))
            kprintf("Initialize Device%d failed\n", i);
        else
            kprintf("Initialize Device%d succeed\n", i);
    }

    kprintf("Initialize IDEController%d...finished\n", uControllerNo);
    return NOERROR;
}

ECode IDEController::InitializeDevice(uint_t uDriveNo)
{
    assert(DRIVES_PER_CONTROLLER > uDriveNo);
    assert(512 ==  sizeof(IDEDeviceInfo));

    IDEDeviceInfo deviceInfo;

    ECode ec = IdentifyDevice(m_iobase, uDriveNo, &deviceInfo);
    if (FAILED(ec)) return ec;

    char szModelNumber[41];
    strncpy(szModelNumber, (char *)deviceInfo.u8ModelNumber, 40);
    szModelNumber[40] = '\0';

    for (int i = 0; i < 40; i += 2) {
        Swap(szModelNumber[i], szModelNumber[i + 1]);
    }

    kprintf("<Drive %d> Cylinders: %d, Heads: %d, SectorsPerTrack: %d\n"
            "   Capability: 0x%x, Model Number: %s\n"
            "   Current Cylinders: %d, Current Heads: %d\n"
            "   Current SectorsPerTrack: %d, Current Sectors: %d\n"
            "   LBA Sectors: %d\n",
            uDriveNo,
            deviceInfo.u16NumberOfLogicalCylinders,
            deviceInfo.u16NumberOfLogicalHeads,
            deviceInfo.u16NumberOfLogicalSectorsPerTrack,
            deviceInfo.u16Capability,
            szModelNumber,
            deviceInfo.u16NumberOfCurrentLogicalCylinders,
            deviceInfo.u16NumberOfCurrentLogicalHeads,
            deviceInfo.u16NumberOfCurrentLogicalSectorsPerTrack,
            MKDWORD(deviceInfo.u16NumberOfCurrentLogicalSectorsLow,
                    deviceInfo.u16NumberOfCurrentLogicalSectorsHigh),
            deviceInfo.u32NumberOfLBASectors);

    // BUG: Need it?
//    InitializeDeviceParameters(m_iobase, uDevice,
//            deviceInfo.u16NumberOfLogicalHeads,
//            deviceInfo.u16NumberOfLogicalSectorsPerTrack - 1);

    // BUG:
    m_driveInfos[uDriveNo].preferredAddressingMode =
        (deviceInfo.u16Capability & IDECapability_LBASupported)?
            AddressingMode_LBA : AddressingMode_CHS;

    m_driveInfos[uDriveNo].bDMASupported =
        (deviceInfo.u16Capability & IDECapability_DMASupported)?
            TRUE : FALSE;

    m_driveInfos[uDriveNo].u16NumberOfLogicalCylinders =
        deviceInfo.u16NumberOfCurrentLogicalCylinders;

    m_driveInfos[uDriveNo].u16NumberOfLogicalHeads =
        deviceInfo.u16NumberOfCurrentLogicalHeads;

    m_driveInfos[uDriveNo].u16NumberOfLogicalSectorsPerTrack =
        deviceInfo.u16NumberOfCurrentLogicalSectorsPerTrack;

    m_driveInfos[uDriveNo].u32NumberOfLBASectors =
        deviceInfo.u32NumberOfLBASectors;

    DzRegisterDevice(DEVICENAME_IDEDRIVE,
            (m_uControllerNo << 1) + uDriveNo, this);

    return NOERROR;
}

ECode IDEController::ReadSectors(
    uint_t uDriveNo, uint32_t u32LogicalSectorNo, uint8_t *pu8Buffer,
    uint_t uNumberOfSectorsToRead, uint_t *puNumberOfSectorsRead)
{
    uint16_t u16CylinderNo = 0;
    uint8_t u8HeadNo = 0;
    uint8_t u8SectorNo = 0;
    uint_t uNumberOfSectorsRead = 0;

    ECode ec = NOERROR;

    uint_t uNumberOfRemainedSectors = uNumberOfSectorsToRead;
    uint_t  uSectorCount = 0;

    *puNumberOfSectorsRead = 0;

    while (uNumberOfRemainedSectors > 0) {
        uSectorCount = (uNumberOfRemainedSectors > SECTOR_COUNT_SIZE)?
                SECTOR_COUNT_SIZE : uNumberOfRemainedSectors;
        uNumberOfRemainedSectors -= uSectorCount;

        switch (m_driveInfos[uDriveNo].preferredAddressingMode) {
            case AddressingMode_CHS:
                u8SectorNo = (u32LogicalSectorNo)
                % m_driveInfos[uDriveNo].u16NumberOfLogicalSectorsPerTrack + 1;

                u8HeadNo = ((u32LogicalSectorNo + 1 - u8SectorNo)
                / m_driveInfos[uDriveNo].u16NumberOfLogicalSectorsPerTrack)
                % m_driveInfos[uDriveNo].u16NumberOfLogicalHeads;

                u16CylinderNo = ((u32LogicalSectorNo + 1 - u8SectorNo)
                / m_driveInfos[uDriveNo].u16NumberOfLogicalSectorsPerTrack
                    - u8HeadNo)
                / m_driveInfos[uDriveNo].u16NumberOfLogicalHeads;
            //check the arguments after translation
                if (m_driveInfos[uDriveNo].u16NumberOfLogicalCylinders
                    <= u16CylinderNo
                    || m_driveInfos[uDriveNo].u16NumberOfLogicalHeads
                        <= u8HeadNo
                    || 0 == u8SectorNo
                    || m_driveInfos[uDriveNo].u16NumberOfLogicalSectorsPerTrack
                    < u8SectorNo) {
                    return E_INVALID_ARGUMENT;
                }
                break;
            case AddressingMode_LBA:
                u16CylinderNo  =
                MKWORD(__BYTE1(u32LogicalSectorNo),
                    __BYTE2(u32LogicalSectorNo));
                u8HeadNo        = __BYTE3(u32LogicalSectorNo);
                u8SectorNo      = __BYTE0(u32LogicalSectorNo);

                if (m_driveInfos[uDriveNo].u32NumberOfLBASectors
                    <= u32LogicalSectorNo) {
                    return E_INVALID_ARGUMENT;
                }
                break;
            default: assert0();
        }

        WaitResult wr;
        m_mutexRW.Lock(&wr);
        if (WaitResult_OK != wr) {
            return NOERROR;
        }
        ec = ::ReadSectors(
            m_iobase, m_driveInfos[uDriveNo].preferredAddressingMode,
            uDriveNo, u16CylinderNo, u8HeadNo, u8SectorNo,
            pu8Buffer, uSectorCount, &uNumberOfSectorsRead,
            &m_eventIsr, &m_nIDECommand);
        m_mutexRW.Unlock();

        *puNumberOfSectorsRead += uNumberOfSectorsRead;

        if (FAILED(ec)) break;

        pu8Buffer += (uSectorCount<<SECTOR_SHIFT);
        u32LogicalSectorNo += uSectorCount;
    }

    return ec;
}

ECode IDEController::WriteSectors(
    uint_t uDriveNo, uint32_t u32LogicalSectorNo, uint8_t *pu8Buffer,
    uint_t uNumberOfSectorsToWrite, uint_t *puNumberOfSectorsWritten)
{
    uint16_t u16CylinderNo = 0;
    uint8_t u8HeadNo = 0;
    uint8_t u8SectorNo = 0;

    uint_t uNumberOfSectorsWritten = 0;

    ECode ec = NOERROR;

    uint_t uNumberOfRemainedSectors = uNumberOfSectorsToWrite;
    uint_t  uSectorCount = 0;

    *puNumberOfSectorsWritten = 0;

    while (uNumberOfRemainedSectors > 0) {
        uSectorCount = (uNumberOfRemainedSectors > SECTOR_COUNT_SIZE)?
                SECTOR_COUNT_SIZE : uNumberOfRemainedSectors;
        uNumberOfRemainedSectors -= uSectorCount;

        switch (m_driveInfos[uDriveNo].preferredAddressingMode) {
            case AddressingMode_CHS:
                u8SectorNo = (u32LogicalSectorNo)
                % m_driveInfos[uDriveNo].u16NumberOfLogicalSectorsPerTrack + 1;

                u8HeadNo = ((u32LogicalSectorNo + 1 - u8SectorNo)
                / m_driveInfos[uDriveNo].u16NumberOfLogicalSectorsPerTrack)
                % m_driveInfos[uDriveNo].u16NumberOfLogicalHeads;

                u16CylinderNo = ((u32LogicalSectorNo + 1 - u8SectorNo)
                / m_driveInfos[uDriveNo].u16NumberOfLogicalSectorsPerTrack
                    - u8HeadNo)
                / m_driveInfos[uDriveNo].u16NumberOfLogicalHeads;

                if (m_driveInfos[uDriveNo].u16NumberOfLogicalCylinders
                    <= u16CylinderNo
                    || m_driveInfos[uDriveNo].u16NumberOfLogicalHeads
                        <= u8HeadNo
                    || 0 == u8SectorNo
                    || m_driveInfos[uDriveNo].u16NumberOfLogicalSectorsPerTrack
                    < u8SectorNo) {
                    return E_INVALID_ARGUMENT;
                }
                break;

            case AddressingMode_LBA:
                u16CylinderNo  =
                MKWORD(__BYTE1(u32LogicalSectorNo),
                    __BYTE2(u32LogicalSectorNo));
                u8HeadNo        = __BYTE3(u32LogicalSectorNo);
                u8SectorNo      = __BYTE0(u32LogicalSectorNo);

                if (m_driveInfos[uDriveNo].u32NumberOfLBASectors
                    <= u32LogicalSectorNo) {
                    return E_INVALID_ARGUMENT;
                }
                break;

            default: assert0();
        }

        WaitResult wr;
        m_mutexRW.Lock(&wr);
        if (WaitResult_OK != wr) {
            return NOERROR;
        }

        ec = ::WriteSectors(
            m_iobase, m_driveInfos[uDriveNo].preferredAddressingMode,
            uDriveNo, u16CylinderNo, u8HeadNo, u8SectorNo,
            pu8Buffer, uSectorCount, &uNumberOfSectorsWritten,
            &m_eventIsr, &m_nIDECommand);

        m_mutexRW.Unlock();

        *puNumberOfSectorsWritten += uNumberOfSectorsWritten;

        if (FAILED(ec)) break;

        pu8Buffer += (uSectorCount<<SECTOR_SHIFT);
        u32LogicalSectorNo += uSectorCount;
    }

    return ec;
}

ECode IDEController::ReadSectors(
    uint_t uDriveNo, uint16_t u16CylinderNo, uint8_t u8HeadNo,
    uint8_t u8SectorNo, uint8_t *pu8Buffer,
    uint_t uNumberOfSectorsToRead, uint_t *puNumberOfSectorsRead)
{
    if (m_driveInfos[uDriveNo].u16NumberOfLogicalCylinders <= u16CylinderNo
        || m_driveInfos[uDriveNo].u16NumberOfLogicalHeads <= u8HeadNo
        || m_driveInfos[uDriveNo].u16NumberOfLogicalSectorsPerTrack
        <= u8SectorNo) {
        return E_INVALID_ARGUMENT;
    }
    //bug
    ECode ec = NOERROR;
    WaitResult wr;
    m_mutexRW.Lock(&wr);
    if (WaitResult_OK != ec) {
        return NOERROR;
    }
    ec = ::ReadSectors(
        m_iobase, AddressingMode_CHS,
        uDriveNo, u16CylinderNo, u8HeadNo, u8SectorNo,
        pu8Buffer, uNumberOfSectorsToRead, puNumberOfSectorsRead,
        &m_eventIsr, &m_nIDECommand);
    m_mutexRW.Unlock();
    return ec;
}

ECode IDEController::WriteSectors(
    uint_t uDriveNo, uint16_t u16CylinderNo, uint8_t u8HeadNo,
    uint8_t u8SectorNo, uint8_t *pu8Buffer,
    uint_t uNumberOfSectorsToWrite, uint_t *puNumberOfSectorsWritten)
{
    if (m_driveInfos[uDriveNo].u16NumberOfLogicalCylinders <= u16CylinderNo
        || m_driveInfos[uDriveNo].u16NumberOfLogicalHeads <= u8HeadNo
        || m_driveInfos[uDriveNo].u16NumberOfLogicalSectorsPerTrack
        <= u8SectorNo) {
        return E_INVALID_ARGUMENT;
    }
    //bug
    ECode ec;

    WaitResult wr;
    m_mutexRW.Lock(&wr);
    if (WaitResult_OK != wr) {
        return NOERROR;
    }
    ec = ::WriteSectors(
        m_iobase, AddressingMode_CHS,
        uDriveNo, u16CylinderNo, u8HeadNo, u8SectorNo,
        pu8Buffer, uNumberOfSectorsToWrite, puNumberOfSectorsWritten,
        &m_eventIsr, &m_nIDECommand);
    m_mutexRW.Unlock();

    return ec;
}

INLINE void IDEController::Isr()
{
    if (m_nIDECommand == IDECommand_ReadSectors ||
        m_nIDECommand == IDECommand_WriteSectors) {
        uint8_t u8Status = Inb(IDEPORT_STATUS(m_iobase));
        if (u8Status == 0x00)
            kprintf("Error!!!");
        m_eventIsr.NotifyByIsr((uint_t)u8Status);
    }
    else {
        assert("unexpection in IDEControlle::ISR");
    }
}

STATIC void CDECL IDEControllerIsr(
    irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    assert(pvDevice);
    ((IDEController *)pvDevice)->Isr();
}

EXTERN IDeviceDriver * CDECL CreateIDEController(uint_t uDeviceNo, void *pvParameter)
{
    IDEController *pIDEController = new IDEController();
    if (NULL == pIDEController) return NULL;

    if (FAILED(pIDEController->Initialize(uDeviceNo))) {
        delete pIDEController;
        return NULL;
    }

    pIDEController->AddRef();

    return pIDEController;
}
